﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using SignalShopBase;

namespace SignalShop
{
    public partial class Chart : UserControl
    {
        private Signal signal;
        public Signal Signal { get { return signal; } set { signal = value; } }

        private bool selected;
        public bool Selected { get { return selected; } set { selected = value; } }

        private PlotMode plotMode;
        public PlotMode PlotMode { get { return plotMode; } set { plotMode = value; } }

        private bool logX;
        public bool LogX { get { return logX; } set { logX = value; } }

        private bool logY;
        public bool LogY { get { return logY; } set { logY = value; } }

        public event ChartEventHandler ChartSelected;

        public Chart()
        {
            InitializeComponent();
        }

        private void Chart_Load(object sender, EventArgs e)
        {
            
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            if (signal != null)
            {
                e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;

                e.Graphics.Clear(Color.White);
                if (selected) e.Graphics.Clear(Color.FromArgb(240, 240, 255));

                int N = signal.N;

                double yMin = double.PositiveInfinity;
                double yMax = double.NegativeInfinity;

                for (int n = -N; n <= N; n++)
                {
                    yMin = Math.Min(yMin, CalcY(signal[n]));
                    yMax = Math.Max(yMax, CalcY(signal[n]));
                }

                double yCenter = (yMin + yMax) / 2.0;
                double yRadius = Math.Abs(yMax - yMin) / 2.0;

                yRadius = Math.Max(double.Epsilon, yRadius);
                yRadius *= 1.1;

                yMin = yCenter - yRadius;
                yMax = yCenter + yRadius;

                for (int n = -N; n < N; n++)
                {
                    int x0 = CalcPX(n, N);
                    int y0 = CalcPY(signal[n], yMin, yMax);

                    int x1 = CalcPX(n + 1, N);
                    int y1 = CalcPY(signal[n + 1], yMin, yMax);

                    e.Graphics.DrawLine(Pens.Black, x0, y0, x1, y1);
                }

                Complex c = new Complex();

                int yZero = logY ? CalcPY(1, yMin, yMax) : CalcPY(0, yMin, yMax);
                
                e.Graphics.DrawLine(Pens.Red, 0, yZero, this.Width - 1, yZero);
            }

            base.OnPaint(e);
        }

        private double CalcX(int n, int N)
        {
            if (logX)
            {
                if (n != 0)
                {
                    double x = (Math.Log(Math.Abs(n)) / Math.Log(N));
                    x *= (N - 1) / (2.0 * N + 1);
                    x += 1.0 / (2.0 * N + 1);

                    return (n < 0) ? -x : x;
                }
                else return 0.0;
            }
            else
            {
                return n / (2.0 * N + 1);
            }
        }

        private int CalcPX(double x)
        {
            return (int)((0.5 + x) * (this.Width - 1));
        }

        private int CalcPX(int n, int N)
        {
            return CalcPX(CalcX(n, N));
        }

        private double CalcY(Complex c)
        {
            if (logY)
            {
                return Math.Log(c.Abs);
            }
            else
            {
                switch (plotMode)
                {
                    case PlotMode.Re:
                        return c.X;
                    case PlotMode.Im:
                        return c.Y;
                    case PlotMode.Abs:
                        return c.Abs;
                    case PlotMode.Arg:
                        return c.Arg;
                    default:
                        return c.X;
                }
            }
        }

        // y: [-0.5, 0.5]
        private int CalcPY(double y, double yMin, double yMax)
        {
            return (int)((yMax - y) * (this.Height - 1) / (yMax - yMin));
        }

        private int CalcPY(Complex c, double yMin, double yMax)
        {
            return CalcPY(CalcY(c), yMin, yMax);
        }

        private void Chart_MouseClick(object sender, MouseEventArgs e)
        {
            this.selected = true;

            if (ChartSelected != null)
            {
                ChartSelected(this, ChartEvent.Selected);
            }

            this.Refresh();
        }        
    }

    public enum PlotMode
    {
        Re,
        Im,
        Abs,
        Arg
    }

    public enum ChartEvent
    {
        Selected
    }

    public delegate void ChartEventHandler(Chart source, ChartEvent e);
}
